package cc.sunni.test;

import cc.sunni.test.serivce.AppleFormatter;
import cc.sunni.test.serivce.BufferedReaderProcessor;
import cc.sunni.test.serivce.impl.AppleFancyFormatter;
import cc.sunni.test.serivce.impl.AppleSimpleFormatter;
import cc.sunni.test.serivce.impl.FilterUtils;
import com.sun.org.apache.bcel.internal.generic.RET;
import com.sun.org.apache.bcel.internal.generic.RETURN;

import java.io.*;
import java.util.*;
import java.util.concurrent.Callable;
import java.util.function.*;
import java.util.stream.Collectors;

/**
 * @author jiangli
 * @since 2021/3/18 8:48
 */
public class Apple {

    private String color;

    private int weight;

    private List<Apple> inventory;

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public List<Apple> getInventory() {
        return inventory;
    }

    public void setInventory(List<Apple> inventory) {
        this.inventory = inventory;
    }

    public Apple() {
    }

    public Apple(int weight) {
        this.weight = weight;
    }

    public Apple(String color, int weight) {
        this.color = color;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "Apple{" +
                "color='" + color + '\'' +
                ", weight=" + weight +
                '}';
    }

    public static boolean isGreenApple(Apple apple) {
        return "green".equals(apple.getColor());
    }

    public static boolean isHeavyApple(Apple apple) {
        return apple.getWeight() > 150;
    }

    public static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
        return result;
    }

    public static void prettyPrintApple(List<Apple> inventory, AppleFormatter appleFormatter) {
        for (Apple apple : inventory) {
            String formatter = appleFormatter.formatter(apple);
            System.out.println(formatter);
        }
    }


    public static void main(String[] args) throws Exception {
        Apple a1 = new Apple("green", 140);
        Apple a3 = new Apple("red", 150);
        Apple a2 = new Apple("yellow", 160);

        List<Apple> inventory = Arrays.asList(a1, a2, a3);

        // 传递方法
        filterApples(inventory, Apple::isGreenApple).forEach(System.out::println);
        filterApples(inventory, Apple::isHeavyApple).forEach(System.out::println);
        System.out.println("==========");

        // 匿名函数,省略了isGreenApple()和isHeavyApple()方法
        filterApples(inventory, a -> "green".equals(a.getColor()));
        filterApples(inventory, a -> 150 > a.weight);
        filterApples(inventory, a -> a.getWeight() < 160 && "red".equals(a.getColor()));

        List<Apple> red = inventory.stream().filter(e -> e.getColor().equals("red")).collect(Collectors.toList());

        prettyPrintApple(inventory, new AppleFancyFormatter());
        System.out.println("==========");
        prettyPrintApple(inventory, new AppleSimpleFormatter());

        System.out.println("==========");
        List<Apple> appleList = FilterUtils.filter(inventory, e -> "green".equals(e.getColor()));
        appleList.forEach(System.out::println);

        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
        List<Integer> evenNumbers = FilterUtils.filter(numbers, i -> i % 2 == 0);
        evenNumbers.forEach(System.out::println);

        Thread t = new Thread(() -> System.out.println("Hello world"));

        System.out.println("==========");
        Callable<String> fetch = fetch();
        String call = fetch.call();
        System.out.println(call);

        System.out.println("==========");
        System.out.println(processFile());
        // 使用lambda行为参数化
        System.out.println(processFile(br -> br.readLine()));
        String twoLines = processFile(br -> br.readLine() + br.readLine());
        System.out.println(twoLines);

        System.out.println("===========");
        List<String> listOfStrings = Arrays.asList("hello", "java", "", "apple");
        List<String> nonEmpty = filter(listOfStrings, s -> !s.isEmpty());
        System.out.println(nonEmpty);

        forEach(Arrays.asList(1, 2, 3, 4, 5), s -> System.out.println(s));

        List<Integer> list = map(Arrays.asList("hello", "java", "sun"), s -> s.length());
        List<String> list1 = map(Arrays.asList("hello", "java", "sun"), s -> s.toUpperCase());
        System.out.println(list);

        List<String> l1 = new ArrayList<>();
        Predicate<String> p = s -> l1.add(s);
        Consumer<String> c = s -> s.toUpperCase();
        Function<String, String> f = s -> s.substring(1, 2);


        // 方法引用
        inventory.sort(Comparator.comparing(e -> e.getWeight())); // 排序
        appleList.sort(Comparator.comparing(Apple::getWeight)  // 按重量排序
                .reversed() // 逆序
                .thenComparing(Apple::getColor)); // 重量一样按颜色排序
        List<String> str = Arrays.asList("a", "b", "A", "B");
        str.sort((s1, s2) -> s1.compareToIgnoreCase(s2));
        str.sort(String::compareToIgnoreCase);

        List<Apple> apples = map(Arrays.asList(149, 150, 151), (weight) -> new Apple(weight));
        // 如果你有一个具有两个参数的构造函数Apple(String color, Integer weight)，那么它就适合BiFunction接口的签名，于是你可以这样写：
        BiFunction<String, Integer, Apple> c3 = Apple::new;
        Apple red1 = c3.apply("red", 150);


        // 函数复合
        Function<Integer, Integer> f1 = x -> x + 1;
        Function<Integer, Integer> f2 = y -> 2 * y;
        Function<Integer, Integer> f3 = f1.andThen(f2);
        // 调用
        Integer apply = f3.apply(1); // 4

    }

    public static Callable<String> fetch() {
        return () -> "Tricky example ;-)";
    }

    public static String processFile() throws IOException {
        try (
                InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\admin\\Desktop\\work.txt"), "GB2312");
                BufferedReader br = new BufferedReader(inputStreamReader)) {
            return br.readLine();
        }
    }

    public static String processFile(BufferedReaderProcessor p) throws IOException {
        try (
                InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\Users\\admin\\Desktop\\work.txt"), "GB2312");
                BufferedReader br = new BufferedReader(inputStreamReader)) {
            return p.process(br);
        }
    }

    public static <T> List<T> filter(List<T> list, Predicate<T> p) {
        List<T> results = new ArrayList<>();
        for (T s : list) {
            if (p.test(s)) {
                results.add(s);
            }
        }
        return results;
    }

    public static <T> void forEach(List<T> list, Consumer<T> c) {
        for (T t : list) {
            c.accept(t);
        }
    }

    public static <T, R> List<R> map(List<T> list, Function<T, R> f) {
        List<R> result = new ArrayList<>();
        for (T t : list) {
            result.add(f.apply(t));
        }
        return result;
    }

}
